﻿using SqlScriptVault.Commands;
using SqlScriptVault.Model;
using SqlScriptVault.Properties;
using SqlScriptVault.Services;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace SqlScriptVault.ViewModel
{
    public class ExecutionProgressViewModel : BaseNotifyViewModel
    {
        private bool _canClose;
        private readonly IMessageDisplayService _messageDisplayService;

        public ExecutionProgressViewModel(IMessageDisplayService messageDisplayService)
        {
            _messageDisplayService = messageDisplayService;
            _canClose = false;
        }

        public ExecutionProgressViewModel()
            : this(new MessageDisplayService())
        {
        }

        public ScriptVaultExecution ScriptVaultExecution { get; set; }

        public Dictionary<Server, SqlCredential> SqlCredentials { get; set; }

        public RelayCommand CloseCommand
        {
            get { return new RelayCommand(param => Close(), param => CanClose()); }
        }

        public event Action RequestClose;

        public void Initialize()
        {
            BackgroundWorker backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += ScriptExecutionMethod;
            backgroundWorker.ProgressChanged += FeedbackMethod;
            backgroundWorker.WorkerReportsProgress = true;

            backgroundWorker.RunWorkerAsync(ScriptVaultExecution);
        }

        private void Close()
        {
            RequestClose();
        }

        private bool CanClose()
        {
            return _canClose;
        }

        private void ScriptExecutionMethod(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker backgroundWorker = (BackgroundWorker)sender;
            ScriptVaultExecution scriptVaultExecution = (ScriptVaultExecution)e.Argument;
            Dictionary<string, List<SqlConnection>> sqlConnections = new Dictionary<string, List<SqlConnection>>();
            Settings settings = new Settings();

            backgroundWorker.ReportProgress(0);

            foreach (Server server in scriptVaultExecution.ServerGroup.Servers)
            {
                if (!scriptVaultExecution.ServerTypes.Contains(server.ServerType))
                {
                    continue;
                }

                if (!sqlConnections.ContainsKey(server.ServerType))
                {
                    sqlConnections.Add(server.ServerType, new List<SqlConnection>());
                }

                SqlConnection sqlConnection;

                if (server.WindowsAuthentication)
                {
                    string connectionString = string.Format(settings.WindowsAuthenticationConnectionString, server.ServerName, server.DatabaseName);
                    sqlConnection = new SqlConnection(connectionString);
                }
                else
                {
                    SqlCredential sqlCredential = SqlCredentials[server];
                    string connectionString = string.Format(settings.ConnectionString, server.ServerName, server.DatabaseName, sqlCredential.Username, sqlCredential.Password);
                    sqlConnection = new SqlConnection(connectionString);
                }

                try
                {
                    sqlConnection.Open();
                }
                catch (Exception exception)
                {
                    _messageDisplayService.ShowErrorMessage(string.Format("Unable to connect to server {0}: {1}", server.FullName, exception.Message));
                    _canClose = true;

                    return;
                }

                sqlConnections[server.ServerType].Add(sqlConnection);
            }

            foreach (Script script in scriptVaultExecution.Scripts)
            {
                try
                {
                    foreach (string serverType in script.ServerTypes)
                    {
                        if (!scriptVaultExecution.ServerTypes.Contains(serverType))
                        {
                            continue;
                        }

                        foreach (SqlConnection sqlConnection in sqlConnections[serverType])
                        {
                            string[] scripts = Regex.Split(script.ScriptContent, settings.ScriptSplitter);

                            foreach (string scriptContent in scripts)
	                        {
                                using (SqlCommand sqlCommand = sqlConnection.CreateCommand())
                                {
                                    sqlCommand.CommandType = CommandType.Text;
                                    sqlCommand.CommandText = scriptContent;
                                    sqlCommand.CommandTimeout = settings.DefaultTimeout;

                                    sqlCommand.ExecuteNonQuery();
                                }
	                        }
                        }
                    }

                    scriptVaultExecution.ScriptSucceeded(script);
                    backgroundWorker.ReportProgress(0);
                }
                catch (Exception exception)
                {
                    scriptVaultExecution.ScriptFailed(script, exception.Message);
                    break;
                }
            }

            backgroundWorker.ReportProgress(0);

            foreach (string serverType in sqlConnections.Keys)
            {
                foreach (SqlConnection sqlConnection in sqlConnections[serverType])
	            {
                    sqlConnection.Close();
                    sqlConnection.Dispose();
	            }
            }

            _canClose = true;
        }

        private void FeedbackMethod(object sender, ProgressChangedEventArgs e)
        {
            RaisePropertyChanged("ScriptVaultExecution");
        }
    }
}
